package com.yox.rpc.transport.socket.client;

import com.yox.rpc.loadBalance.LoadBalancer;
import com.yox.rpc.loadBalance.RandomLoadBalancer;
import com.yox.rpc.registry.NacosServiceDiscover;
import com.yox.rpc.registry.ServiceDiscover;
import com.yox.rpc.transport.RpcClient;
import com.yox.rpc.entity.RpcRequest;
import com.yox.rpc.entity.RpcResponse;
import com.yox.rpc.enumeration.ResponseCode;
import com.yox.rpc.enumeration.RpcError;
import com.yox.rpc.exception.RpcException;
import com.yox.rpc.serializer.CommonSerializer;
import com.yox.rpc.transport.socket.util.ObjectReader;
import com.yox.rpc.transport.socket.util.ObjectWriter;
import com.yox.rpc.util.RpcMessageChecker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;

/**
 * 客户端
 * 建立 sokekt 链接，发送 rpcRequest 序列化请求对象给服务器，得到一个响应值，使用 getObject() 获取这个响应对象 RpcResponse
 */
public class SocketClient implements RpcClient {

    private static Logger logger = LoggerFactory.getLogger(SocketClient.class);

    private final ServiceDiscover serviceDiscover;
    private final CommonSerializer serializer;

    public SocketClient() {
        this(DEFAULT_SERIALIZER, new RandomLoadBalancer());
    }

    public SocketClient(LoadBalancer loadBalancer) {
        this(DEFAULT_SERIALIZER, loadBalancer);
    }

    public SocketClient(Integer serializerID) {
        this(serializerID, new RandomLoadBalancer());
    }

    public SocketClient(Integer serializerID, LoadBalancer loadBalancer) {
        this.serviceDiscover = new NacosServiceDiscover(loadBalancer);
        this.serializer = CommonSerializer.getByCode(serializerID);
    }

    @Override
    public Object sendRequest(RpcRequest rpcRequest) {
        if (serializer == null) {
            logger.error("未设置序列化器");
            throw new RpcException(RpcError.SERVICE_NOT_FOUND);
        }
        InetSocketAddress ip = serviceDiscover.lookupService(rpcRequest.getInterfaceName());
        try (Socket socket = new Socket()) {
            socket.connect(ip);
            // 创建 socket 输入输出流
            InputStream in = socket.getInputStream();
            OutputStream out = socket.getOutputStream();

            // 使用 ObjectWriter 发送 RpcRequest 序列化对象
            ObjectWriter.writeObject(out, rpcRequest, serializer);

            // 使用 ObjectReader 接收响应值
            Object obj = ObjectReader.readObject(in);
            RpcResponse rpcResponse = (RpcResponse) obj;
            RpcMessageChecker.check(rpcRequest, rpcResponse);
            if (rpcResponse == null) {
                logger.error("服务调用失败，service：{}", rpcRequest.getInterfaceName());
                throw new RpcException(RpcError.SERVICE_INVOVATION_FAILURE, "service:" + rpcRequest.getInterfaceName());
            }

            if (rpcResponse.getStatusCode() == null || rpcResponse.getStatusCode() != ResponseCode.SUCCESS.getCode()) {
                logger.error("调用服务失败，service：{}, response: {}", rpcRequest.getInterfaceName(), rpcResponse);
            }

            // 检查
            RpcMessageChecker.check(rpcRequest, rpcResponse);

            // 返回 socket 接受到的response对象
            return rpcResponse;
        } catch (IOException e) {
            logger.error("调用时有发生错误", e);
            throw new RpcException("服务调用失败", e);
        }
    }
}
